// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "common/text/token_info_test_util.h"

#include <vector>

#include "absl/strings/string_view.h"
#include "common/text/token_info.h"
#include "gtest/gtest.h"

namespace verible {
namespace {

TEST(TokenInfoTestDataTest, EmptyInitializer) {
  const TokenInfoTestData test_data{};
  EXPECT_TRUE(test_data.code.empty());
  EXPECT_TRUE(test_data.expected_tokens.empty());
}

TEST(TokenInfoTestDataTest, ConstructorStringLiteral) {
  const TokenInfoTestData test_data = {
      {"foo"},
      {"bar"},
  };
  EXPECT_EQ(test_data.code, "foobar");

  const auto &first(test_data.expected_tokens.front());
  EXPECT_EQ(first.text(), "foo");
  EXPECT_EQ(first.token_enum(), ExpectedTokenInfo::kDontCare);
  EXPECT_EQ(first.left(test_data.code), 0);
  EXPECT_EQ(first.right(test_data.code), 3);

  const auto &second(test_data.expected_tokens.back());
  EXPECT_EQ(second.text(), "bar");
  EXPECT_EQ(second.token_enum(), ExpectedTokenInfo::kDontCare);
  EXPECT_EQ(second.left(test_data.code), 3);
  EXPECT_EQ(second.right(test_data.code), 6);
}

TEST(ExpectedTokenInfoTest, ConstructorCharEnum) {
  ExpectedTokenInfo token(';');
  EXPECT_EQ(token.token_enum(), ';');
  EXPECT_EQ(token.text(), ";");
  EXPECT_EQ(token.left(token.text()), 0);
  EXPECT_EQ(token.right(token.text()), 1);
}

TEST(TokenInfoTestDataTest, ConstructorCharEnum) {
  const TokenInfoTestData test_data = {
      {'('},
      {')'},
  };
  EXPECT_EQ(test_data.code, "()");

  const auto &first(test_data.expected_tokens.front());
  EXPECT_EQ(first.text(), "(");
  EXPECT_EQ(first.token_enum(), '(');
  EXPECT_EQ(first.left(test_data.code), 0);
  EXPECT_EQ(first.right(test_data.code), 1);

  const auto &second(test_data.expected_tokens.back());
  EXPECT_EQ(second.text(), ")");
  EXPECT_EQ(second.token_enum(), ')');
  EXPECT_EQ(second.left(test_data.code), 1);
  EXPECT_EQ(second.right(test_data.code), 2);
}

TEST(ExpectedTokenInfoTest, ConstructorEnumAndStringLiteral) {
  ExpectedTokenInfo token(11, "f00");
  EXPECT_EQ(token.token_enum(), 11);
  EXPECT_EQ(token.text(), "f00");
}

TEST(TokenInfoTestDataTest, ConstructorEnumAndStringLiteral) {
  const TokenInfoTestData test_data = {
      {8, "foo"},
      {7, "bar"},
  };
  EXPECT_EQ(test_data.code, "foobar");

  const auto &first(test_data.expected_tokens.front());
  EXPECT_EQ(first.text(), "foo");
  EXPECT_EQ(first.token_enum(), 8);
  EXPECT_EQ(first.left(test_data.code), 0);
  EXPECT_EQ(first.right(test_data.code), 3);

  const auto &second(test_data.expected_tokens.back());
  EXPECT_EQ(second.text(), "bar");
  EXPECT_EQ(second.token_enum(), 7);
  EXPECT_EQ(second.left(test_data.code), 3);
  EXPECT_EQ(second.right(test_data.code), 6);
}

TEST(TokenInfoTestDataTest, ConstructorHeterogeneousInitializer) {
  // This example best represents a typical lexer test case that verifies a
  // sequence of tokens.  The token enumeration values would realistically be
  // written as enumeration values generated by yacc/bison.
  const TokenInfoTestData test_data = {{8, "foo"}, " ", '(', {7, "bar"}, ')'};
  EXPECT_EQ(test_data.code, "foo (bar)");
  const auto &expected_tokens = test_data.expected_tokens;
  ASSERT_EQ(expected_tokens.size(), 5);

  EXPECT_EQ(expected_tokens[0].text(), "foo");
  EXPECT_EQ(expected_tokens[0].token_enum(), 8);
  EXPECT_EQ(expected_tokens[0].left(test_data.code), 0);
  EXPECT_EQ(expected_tokens[0].right(test_data.code), 3);

  EXPECT_EQ(expected_tokens[1].text(), " ");
  EXPECT_EQ(expected_tokens[1].token_enum(), ExpectedTokenInfo::kDontCare);
  EXPECT_EQ(expected_tokens[1].left(test_data.code), 3);
  EXPECT_EQ(expected_tokens[1].right(test_data.code), 4);

  EXPECT_EQ(expected_tokens[2].text(), "(");
  EXPECT_EQ(expected_tokens[2].token_enum(), '(');
  EXPECT_EQ(expected_tokens[2].left(test_data.code), 4);
  EXPECT_EQ(expected_tokens[2].right(test_data.code), 5);

  EXPECT_EQ(expected_tokens[3].text(), "bar");
  EXPECT_EQ(expected_tokens[3].token_enum(), 7);
  EXPECT_EQ(expected_tokens[3].left(test_data.code), 5);
  EXPECT_EQ(expected_tokens[3].right(test_data.code), 8);

  EXPECT_EQ(expected_tokens[4].text(), ")");
  EXPECT_EQ(expected_tokens[4].token_enum(), ')');
  EXPECT_EQ(expected_tokens[4].left(test_data.code), 8);
  EXPECT_EQ(expected_tokens[4].right(test_data.code), 9);
}

TEST(TokenInfoTestDataTest, FindImportantTokensTest) {
  // This example best represents a typical lexer test case that verifies a
  // sequence of tokens.  The token enumeration values would realistically be
  // written as enumeration values generated by yacc/bison.
  const TokenInfoTestData test_data = {
      {8, "foo"}, " ", '(', {7, "bar"}, " ", "/* comment */", ')',
  };
  EXPECT_EQ(test_data.code, "foo (bar /* comment */)");
  const auto &expected_tokens = test_data.expected_tokens;
  EXPECT_EQ(expected_tokens.size(), 7);
  const auto key_tokens = test_data.FindImportantTokens();
  EXPECT_EQ(key_tokens.size(), 4);

  EXPECT_EQ(key_tokens[0].text(), "foo");
  EXPECT_EQ(key_tokens[0].token_enum(), 8);
  EXPECT_EQ(key_tokens[0].left(test_data.code), 0);
  EXPECT_EQ(key_tokens[0].right(test_data.code), 3);

  EXPECT_EQ(key_tokens[1].text(), "(");
  EXPECT_EQ(key_tokens[1].token_enum(), '(');
  EXPECT_EQ(key_tokens[1].left(test_data.code), 4);
  EXPECT_EQ(key_tokens[1].right(test_data.code), 5);

  EXPECT_EQ(key_tokens[2].text(), "bar");
  EXPECT_EQ(key_tokens[2].token_enum(), 7);
  EXPECT_EQ(key_tokens[2].left(test_data.code), 5);
  EXPECT_EQ(key_tokens[2].right(test_data.code), 8);

  EXPECT_EQ(key_tokens[3].text(), ")");
  EXPECT_EQ(key_tokens[3].token_enum(), ')');
  EXPECT_EQ(key_tokens[3].left(test_data.code), 22);
  EXPECT_EQ(key_tokens[3].right(test_data.code), 23);
}

TEST(TokenInfoTestDataTest, RebaseToCodeCopyEmpty) {
  std::vector<TokenInfo> tokens;
  const TokenInfoTestData test_data{};
  constexpr absl::string_view other_text("");
  EXPECT_EQ(test_data.code, other_text);
  test_data.RebaseToCodeCopy(&tokens, other_text);
  EXPECT_TRUE(tokens.empty());
}

TEST(TokenInfoTestDataTest, RebaseToCodeCopyMoved) {
  const TokenInfoTestData test_data{
      {3, "text"},
      {4, "book"},
  };
  constexpr absl::string_view other_text("textbook");         // separate copy
  std::vector<TokenInfo> tokens = test_data.expected_tokens;  // copy
  EXPECT_EQ(test_data.code, other_text);
  EXPECT_EQ(tokens.size(), 2);

  EXPECT_EQ(tokens[0].left(test_data.code), 0);
  EXPECT_EQ(tokens[0].right(test_data.code), 4);
  EXPECT_EQ(tokens[1].left(test_data.code), 4);
  EXPECT_EQ(tokens[1].right(test_data.code), 8);

  test_data.RebaseToCodeCopy(&tokens, other_text);

  // Note: expected locations are now based off of other_text.
  EXPECT_EQ(tokens[0].text(), "text");
  EXPECT_EQ(tokens[0].token_enum(), 3);
  EXPECT_EQ(tokens[0].left(other_text), 0);
  EXPECT_EQ(tokens[0].right(other_text), 4);

  EXPECT_EQ(tokens[1].text(), "book");
  EXPECT_EQ(tokens[1].token_enum(), 4);
  EXPECT_EQ(tokens[1].left(other_text), 4);
  EXPECT_EQ(tokens[1].right(other_text), 8);
}

TEST(TokenInfoTestDataTest, FindImportantTokensRebased) {
  const TokenInfoTestData test_data{
      {3, "text"},
      {4, "book"},
  };
  constexpr absl::string_view other_text("textbook");  // separate copy
  std::vector<TokenInfo> tokens = test_data.FindImportantTokens(other_text);
  EXPECT_EQ(test_data.code, other_text);
  EXPECT_EQ(tokens.size(), 2);

  // Note: expected locations are now based off of other_text.
  EXPECT_EQ(tokens[0].text(), "text");
  EXPECT_EQ(tokens[0].token_enum(), 3);
  EXPECT_EQ(tokens[0].left(other_text), 0);
  EXPECT_EQ(tokens[0].right(other_text), 4);

  EXPECT_EQ(tokens[1].text(), "book");
  EXPECT_EQ(tokens[1].token_enum(), 4);
  EXPECT_EQ(tokens[1].left(other_text), 4);
  EXPECT_EQ(tokens[1].right(other_text), 8);
}

}  // namespace
}  // namespace verible
